// Copyright (c) 2004  David Muse
// See the file COPYING for more information

#include <sqlrelay/sqlrclient.h>
#include <rudiments/charstring.h>
#include <rudiments/rawbuffer.h>

#define NEED_DATATYPESTRING 1
#define NEED_IS_NUMBER_TYPE_CHAR 1
#define NEED_IS_UNSIGNED_TYPE_CHAR 1
#define NEED_IS_BINARY_TYPE_CHAR 1
#include <datatypes.h>

extern "C" {

//#define DEBUG_MESSAGES 1
#ifdef DEBUG_MESSAGES
	#define debugFunction() printf("%s:%s():%d:\n",__FILE__,__FUNCTION__,__LINE__); fflush(stdout);
	#define debugPrintf(args) printf(args); fflush(stdout);
#else
	#define debugFunction() /* */
	#define debugPrintf(args) /* */
#endif

#define CR_UNKNOWN_ERROR	2000
#define MYSQL_NO_DATA		100
#define REFRESH_GRANT		1

typedef unsigned long long	my_ulonglong;
typedef bool			my_bool;
typedef my_ulonglong *		MYSQL_ROW_OFFSET;
typedef unsigned int		MYSQL_FIELD_OFFSET;

enum enum_mysql_set_option { MYSQL_SET_OPTION_UNKNOWN_OPTION };
enum mysql_option { MYSQL_OPTION_UNKNOWN_OPTION };

// Taken directly from mysql_com.h version 5.1.22
// Back-compatible with all previous versions.
enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY,
			MYSQL_TYPE_SHORT,  MYSQL_TYPE_LONG,
			MYSQL_TYPE_FLOAT,  MYSQL_TYPE_DOUBLE,
			MYSQL_TYPE_NULL,   MYSQL_TYPE_TIMESTAMP,
			MYSQL_TYPE_LONGLONG,MYSQL_TYPE_INT24,
			MYSQL_TYPE_DATE,   MYSQL_TYPE_TIME,
			MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
			MYSQL_TYPE_NEWDATE,
			MYSQL_TYPE_ENUM=247,
			MYSQL_TYPE_SET=248,
			MYSQL_TYPE_TINY_BLOB=249,
			MYSQL_TYPE_MEDIUM_BLOB=250,
			MYSQL_TYPE_LONG_BLOB=251,
			MYSQL_TYPE_BLOB=252,
			MYSQL_TYPE_VAR_STRING=253,
			MYSQL_TYPE_STRING=254,
			MYSQL_TYPE_GEOMETRY=255
};

#ifdef COMPAT_MYSQL_3
// taken directly from mysql.h - 3.23.58
struct MYSQL_FIELD {
  char *name;			/* Name of column */
  char *table;			/* Table of column if column was a field */
  char *def;			/* Default value (set by mysql_list_fields) */
  enum enum_field_types type;	/* Type of field. Se mysql_com.h for types */
  unsigned int length;		/* Width of column */
  unsigned int max_length;	/* Max width of selected set */
  unsigned int flags;		/* Div flags */
  unsigned int decimals;	/* Number of decimals in field */
};
#endif

#ifdef COMPAT_MYSQL_4_0
// taken directly from mysql.h - 4.0.17
struct MYSQL_FIELD {
  char *name;			/* Name of column */
  char *table;			/* Table of column if column was a field */
  char *org_table;		/* Org table name if table was an alias */
  char *db;			/* Database for table */
  char *def;			/* Default value (set by mysql_list_fields) */
  unsigned long length;		/* Width of column */
  unsigned long max_length;	/* Max width of selected set */
  unsigned int flags;		/* Div flags */
  unsigned int decimals;	/* Number of decimals in field */
  enum enum_field_types type;	/* Type of field. Se mysql_com.h for types */
};
#endif

#if defined(COMPAT_MYSQL_4_1) || \
	defined(COMPAT_MYSQL_5_0) || \
	defined(COMPAT_MYSQL_5_1)
// taken directly from mysql.h - 4.1.1-alpha (5.0.0-alpha is the same)
struct MYSQL_FIELD {
  char *name;                 /* Name of column */
  char *org_name;             /* Original column name, if an alias */ 
  char *table;                /* Table of column if column was a field */
  char *org_table;            /* Org table name, if table was an alias */
  char *db;                   /* Database for table */
  char *catalog;	      /* Catalog for table */
  char *def;                  /* Default value (set by mysql_list_fields) */
  unsigned long length;       /* Width of column */
  unsigned long max_length;   /* Max width of selected set */
  unsigned int name_length;
  unsigned int org_name_length;
  unsigned int table_length;
  unsigned int org_table_length;
  unsigned int db_length;
  unsigned int catalog_length;
  unsigned int def_length;
  unsigned int flags;         /* Div flags */
  unsigned int decimals;      /* Number of decimals in field */
  unsigned int charsetnr;     /* Character set */
  enum enum_field_types type; /* Type of field. Se mysql_com.h for types */
#ifdef COMPAT_MYSQL_5_1
  // taken directly from mysql.h - 5.1.22
  void *extension;
#endif
};
#endif

#ifdef COMPAT_MYSQL_5_1
// taken from mysql.h - 5.1.22, modified
typedef struct st_mysql_bind {
  unsigned long	*length;          /* output length pointer */
  my_bool       *is_null;	  /* Pointer to null indicator */
  //void		*buffer;	  /* buffer to get/put data */
  char		*buffer;
  /* set this if you want to track data truncations happened during fetch */
  my_bool       *error;
  unsigned char *row_ptr;         /* for the current data position */
  //void (*store_param_func)(NET *net, struct st_mysql_bind *param);
  void (*store_param_func)(void *net, struct st_mysql_bind *param);
  void (*fetch_result)(struct st_mysql_bind *, MYSQL_FIELD *,
                       unsigned char **row);
  void (*skip_result)(struct st_mysql_bind *, MYSQL_FIELD *,
		      unsigned char **row);
  /* output buffer length, must be set when fetching str/binary */
  unsigned long buffer_length;
  unsigned long offset;           /* offset position for char/binary fetch */
  unsigned long	length_value;     /* Used if length is 0 */
  unsigned int	param_number;	  /* For null count and error messages */
  unsigned int  pack_length;	  /* Internal length for packed data */
  enum enum_field_types buffer_type;	/* buffer type */
  my_bool       error_value;      /* used if error is 0 */
  my_bool       is_unsigned;      /* set if integer type is unsigned */
  my_bool	long_data_used;	  /* If used with mysql_send_long_data */
  my_bool	is_null_value;    /* Used if is_null is 0 */
  void *extension;
} MYSQL_BIND;
#else
// taken directly from mysql.h - 5.0
struct MYSQL_BIND {
  unsigned long	*length;          /* output length pointer */
  my_bool       *is_null;	  /* Pointer to null indicators */
  char		*buffer;	  /* buffer to get/put data */
  enum enum_field_types buffer_type;	/* buffer type */
  unsigned long buffer_length;    /* buffer length, must be set for str/binary */  

  /* Following are for internal use. Set by mysql_bind_param */
  unsigned char *inter_buffer;    /* for the current data position */
  unsigned long offset;           /* offset position for char/binary fetch */
  unsigned long	internal_length;  /* Used if length is 0 */
  unsigned int	param_number;	  /* For null count and error messages */
  my_bool	long_data_used;	  /* If used with mysql_send_long_data */
  my_bool       binary_data;      /* data buffer is binary */
  my_bool       null_field;       /* NULL data cache flag */
  my_bool	internal_is_null; /* Used if is_null is 0 */
  void (*store_param_func);/*(NET *net, struct MYSQL_BIND *param);*/
  void (*fetch_result);/*(struct MYSQL_BIND *, unsigned char **row);*/
};
#endif

// This is the same for all versions of mysql that I've ever seen
typedef char **MYSQL_ROW;

struct MYSQL_RES {
	sqlrcursor		*sqlrcur;
	// don't call this errno, some systems have a macro for errno
	// which will get substituted in for all errno references and give
	// undesirable results
	unsigned int		errorno;
	my_ulonglong		previousrow;
	my_ulonglong		currentrow;
	MYSQL_FIELD_OFFSET	currentfield;
	MYSQL_FIELD		*fields;
	unsigned long		*lengths;
};

struct MYSQL_STMT {
	MYSQL_RES	*result;
	MYSQL_BIND	*resultbinds;
};

// FIXME: apps access this directly! so it must be the same size and have
// same members as the real struct MYSQL
struct MYSQL {
	const char	*host;
	unsigned int	port;
	const char	*unix_socket;
	sqlrconnection	*sqlrcon;
	MYSQL_STMT	*currentstmt;
	bool		deleteonclose;
	const char	*error;
	int		errorno;
};


unsigned int mysql_thread_safe();
MYSQL *mysql_init(MYSQL *mysql);
int mysql_set_server_option(MYSQL *mysql, enum enum_mysql_set_option option);
int mysql_options(MYSQL *mysql, enum mysql_option option, const char *arg);
int mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert,
			const char *ca, const char *capath,
			const char *cipher);
MYSQL *mysql_connect(MYSQL *mysql, const char *host,
			const char *user, const char *passwd);
MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user,
				const char *passwd, const char *db,
				unsigned int port, const char *unix_socket,
				unsigned long client_flag);
void mysql_close(MYSQL *mysql);
int mysql_ping(MYSQL *mysql);
char *mysql_stat(MYSQL *mysql);
int mysql_shutdown(MYSQL *mysql);
int mysql_reload(MYSQL *mysql);
int mysql_refresh(MYSQL *mysql, unsigned int refresh_options);
unsigned long mysql_thread_id(MYSQL *mysql);
MYSQL_RES *mysql_list_processes(MYSQL *mysql);
int mysql_kill(MYSQL *mysql, unsigned long pid);
char *mysql_get_client_info();
unsigned long mysql_get_client_version();
char *mysql_get_host_info(MYSQL *mysql);
unsigned int mysql_get_proto_info(MYSQL *mysql);
char *mysql_get_server_info(MYSQL *mysql);
unsigned long mysql_get_server_version(MYSQL *mysql);
my_bool	mysql_change_user(MYSQL *mysql, const char *user,
				const char *password, const char *db);
const char *mysql_character_set_name(MYSQL *mysql);
int mysql_set_character_set(MYSQL *mysql, const char *csname);
void mysql_debug(const char *debug);
int mysql_dump_debug_info(MYSQL *mysql);
int mysql_create_db(MYSQL *mysql, const char *db);
int mysql_select_db(MYSQL *mysql, const char *db);
int mysql_drop_db(MYSQL *mysql, const char *db);
MYSQL_RES *mysql_list_dbs(MYSQL *mysql, const char *wild);
MYSQL_RES *mysql_list_tables(MYSQL *mysql, const char *wild);
unsigned long mysql_escape_string(char *to, const char *from,
					unsigned long length);
char *mysql_odbc_escape_string(MYSQL *mysql, char *to,
				unsigned long to_length,
				const char *from,
				unsigned long from_length,
				void *param,
				char *(*extend_buffer)
					(void *, char *to,
					unsigned long *length));
void myodbc_remove_escape(MYSQL *mysql, char *name);
int mysql_query(MYSQL *mysql, const char *query);
int mysql_send_query(MYSQL *mysql, const char *query, unsigned int length);
int mysql_read_query_result(MYSQL *mysql);
unsigned long mysql_real_escape_string(MYSQL *mysql, char *to,
					const char *from,
					unsigned long length);
int mysql_real_query(MYSQL *mysql, const char *query, unsigned long length);
char *mysql_info(MYSQL *mysql);
my_ulonglong mysql_insert_id(MYSQL *mysql);
MYSQL_RES *mysql_store_result(MYSQL *mysql);
MYSQL_RES *mysql_use_result(MYSQL *mysql);
void mysql_free_result(MYSQL_RES *result);
my_bool mysql_more_results(MYSQL *mysql);
int mysql_next_result(MYSQL *mysql);
MYSQL_RES *mysql_list_fields(MYSQL *mysql, const char *table,
						const char *wild);
unsigned int mysql_num_fields(MYSQL_RES *result);
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result);
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *result);
MYSQL_FIELD *mysql_fetch_field_direct(MYSQL_RES *result, unsigned int fieldnr);
unsigned long *mysql_fetch_lengths(MYSQL_RES *result);
unsigned int mysql_field_count(MYSQL *mysql);
MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result,
					MYSQL_FIELD_OFFSET offset);
MYSQL_FIELD_OFFSET mysql_field_tell(MYSQL_RES *result);
my_ulonglong mysql_num_rows(MYSQL_RES *result);
my_ulonglong mysql_affected_rows(MYSQL *mysql);
MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET offset);
MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *result);
void mysql_data_seek(MYSQL_RES *result, my_ulonglong offset);
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
my_bool mysql_eof(MYSQL_RES *result);
unsigned int mysql_warning_count(MYSQL *mysql);
unsigned int mysql_errno(MYSQL *mysql);
const char *mysql_error(MYSQL *mysql);
const char *mysql_sqlstate(MYSQL *mysql);
my_bool mysql_commit(MYSQL *mysql);
my_bool mysql_rollback(MYSQL *mysql);
my_bool mysql_autocommit(MYSQL *mysql, my_bool mode);
MYSQL_STMT *mysql_prepare(MYSQL *mysql, const char *query,
					unsigned long length);
my_bool mysql_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind);
my_bool mysql_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind);
int mysql_execute(MYSQL_STMT *stmt);
unsigned long mysql_param_count(MYSQL_STMT *stmt);
MYSQL_RES *mysql_param_result(MYSQL_STMT *stmt);
int mysql_fetch(MYSQL_STMT *stmt);
int mysql_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind,
			unsigned int column, unsigned long offset);
MYSQL_RES *mysql_get_metadata(MYSQL_STMT *stmt);
my_bool mysql_send_long_data(MYSQL_STMT *stmt,
				unsigned int parameter_number,
				const char *data, unsigned long length);
my_ulonglong mysql_stmt_num_rows(MYSQL_STMT *stmt);
my_ulonglong mysql_stmt_affected_rows(MYSQL_STMT *stmt);
MYSQL_ROW_OFFSET mysql_stmt_row_seek(MYSQL_STMT *stmt,
					MYSQL_ROW_OFFSET offset);
MYSQL_ROW_OFFSET mysql_stmt_row_tell(MYSQL_STMT *stmt);
void mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong offset);
my_bool mysql_stmt_close(MYSQL_STMT *stmt);
unsigned int mysql_stmt_errno(MYSQL_STMT *stmt);
const char *mysql_stmt_error(MYSQL_STMT *stmt);
const char *mysql_stmt_sqlstate(MYSQL_STMT *stmt);
int mysql_stmt_store_result(MYSQL_STMT *stmt);
my_bool mysql_stmt_free_result(MYSQL_STMT *stmt);
my_bool mysql_stmt_reset(MYSQL_STMT *stmt);
void mysql_server_init(int argc, char **argv, char **groups);
void mysql_library_init(int argc, char **argv, char **groups);
void mysql_server_end();
void mysql_library_end();

static int unknownError(MYSQL *mysql);



unsigned int mysql_thread_safe() {
	debugFunction();
	return 1;
}

MYSQL *mysql_init(MYSQL *mysql) {
	debugFunction();
	if (mysql) {
		rawbuffer::zero(mysql,sizeof(MYSQL));
		return mysql;
	} else {
		MYSQL	*retval=new MYSQL;
		rawbuffer::zero(retval,sizeof(MYSQL));
		return retval;
	}
}

int mysql_set_server_option(MYSQL *mysql, enum enum_mysql_set_option option) {
	debugFunction();
	return 0;
}

int mysql_options(MYSQL *mysql, enum mysql_option option, const char *arg) {
	debugFunction();
	return 0;
}

int mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert,
			const char *ca, const char *capath,
			const char *cipher) {
	debugFunction();
	return 0;
}

MYSQL *mysql_connect(MYSQL *mysql, const char *host,
			const char *user, const char *passwd) {
	debugFunction();
	return mysql_real_connect(mysql,host,user,passwd,NULL,9000,NULL,0);
}

MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user,
				const char *passwd, const char *db,
				unsigned int port, const char *unix_socket,
				unsigned long client_flag) {
	debugFunction();

	mysql->host=host;
	mysql->port=port;
	mysql->unix_socket=unix_socket;

	mysql->sqlrcon=new sqlrconnection(host,port,unix_socket,
						user,passwd,0,1);
//mysql->sqlrcon->debugOn();
	mysql->sqlrcon->copyReferences();
	mysql->currentstmt=NULL;
	return mysql;
}

void mysql_close(MYSQL *mysql) {
	debugFunction();
	if (mysql) {
		mysql_stmt_close(mysql->currentstmt);
		delete mysql->sqlrcon;
		if (mysql->deleteonclose) {
			delete mysql;
		}
	}
}


int mysql_ping(MYSQL *mysql) {
	debugFunction();
	return !mysql->sqlrcon->ping();
}

char *mysql_stat(MYSQL *mysql) {
	debugFunction();
	return "Uptime: 0  Threads: 0  Questions: 0  Slow queries: 0  Opens: 0  Flush tables: 0  Open tables: 0 Queries per second avg: 0.0";
}

int mysql_shutdown(MYSQL *mysql) {
	debugFunction();
	return unknownError(mysql);
}

int mysql_refresh(MYSQL *mysql, unsigned int refresh_options) {
	debugFunction();
	return (refresh_options==REFRESH_GRANT)?mysql_reload(mysql):0;
}

int mysql_reload(MYSQL *mysql) {
	debugFunction();
	if (!charstring::compare(mysql->sqlrcon->identify(),"mysql")) {
		sqlrcursor	sqlrcur(mysql->sqlrcon);
		return !sqlrcur.sendQuery("FLUSH PRIVILEGES");
	}
	return 0;
}

unsigned long mysql_thread_id(MYSQL *mysql) {
	debugFunction();
	return 0;
}

MYSQL_RES *mysql_list_processes(MYSQL *mysql) {
	debugFunction();
	return NULL;
}

int mysql_kill(MYSQL *mysql, unsigned long pid) {
	debugFunction();
	return unknownError(mysql);
}



char *mysql_get_client_info() {
	debugFunction();
	// Returns a string that represents the client library version.
	#ifdef COMPAT_MYSQL_3
		return "3.23.58";
	#endif
	#ifdef COMPAT_MYSQL_4_0
		return "4.0.17";
	#endif
	#ifdef COMPAT_MYSQL_4_1
		return "4.1.1";
	#endif
	#ifdef COMPAT_MYSQL_5_0
		return "5.0.0";
	#endif
	#ifdef COMPAT_MYSQL_5_1
		return "5.1.22";
	#endif
}

unsigned long mysql_get_client_version() {
	debugFunction();
	// Returns an integer that represents the client library version.
	// The value has the format XYYZZ where X is the major version, YY is
	// the release level, and ZZ is the version number within the release
	// level. For example, a value of 40102 represents a client library
	// version of 4.1.2.
	#ifdef COMPAT_MYSQL_3
		return 32358;
	#endif
	#ifdef COMPAT_MYSQL_4_0
		return 40017;
	#endif
	#ifdef COMPAT_MYSQL_4_1
		return 40101;
	#endif
	#ifdef COMPAT_MYSQL_5_0
		return 50000;
	#endif
	#ifdef COMPAT_MYSQL_5_1
		return 50122;
	#endif
}

char *mysql_get_host_info(MYSQL *mysql) {
	debugFunction();
	// Returns a string describing the type of connection in use,
	// including the server host name.
	// Should be "host via [unix|inet] socket"
	return "";
}

unsigned int mysql_get_proto_info(MYSQL *mysql) {
	debugFunction();
	// Returns the protocol version used by current connection.
	#ifdef COMPAT_MYSQL_3
		return 10;
	#endif
	#ifdef COMPAT_MYSQL_4_0
		return 12;
	#endif
	#ifdef COMPAT_MYSQL_4_1
		return 14;
	#endif
	#ifdef COMPAT_MYSQL_5_0
		return 14;
	#endif
	#ifdef COMPAT_MYSQL_5_1
		return 10;
	#endif
}

char *mysql_get_server_info(MYSQL *mysql) {
	debugFunction();
	// Returns a string that represents the server version number.
	#ifdef COMPAT_MYSQL_3
		return "3.23.58";
	#endif
	#ifdef COMPAT_MYSQL_4_0
		return "4.0.17";
	#endif
	#ifdef COMPAT_MYSQL_4_1
		return "4.1.1";
	#endif
	#ifdef COMPAT_MYSQL_5_0
		return "5.0.0";
	#endif
	#ifdef COMPAT_MYSQL_5_1
		return "5.1.22";
	#endif
}

unsigned long mysql_get_server_version(MYSQL *mysql) {
	debugFunction();
	// A number that represents the MySQL server version in format:
	// main_version*10000 + minor_version *100 + sub_version
	// For example, 4.1.0 is returned as 40100.
	#ifdef COMPAT_MYSQL_3
		return 32358;
	#endif
	#ifdef COMPAT_MYSQL_4_0
		return 40017;
	#endif
	#ifdef COMPAT_MYSQL_4_1
		return 40101;
	#endif
	#ifdef COMPAT_MYSQL_5_0
		return 50000;
	#endif
	#ifdef COMPAT_MYSQL_5_1
		return 50122;
	#endif
}



my_bool	mysql_change_user(MYSQL *mysql, const char *user,
				const char *password, const char *db) {
	debugFunction();

	if (!mysql->sqlrcon->rollback()) {
		return false;
	}
	mysql_stmt_close(mysql->currentstmt);
	delete mysql->sqlrcon;
	return (mysql_real_connect(mysql,mysql->host,user,password,db,
				mysql->port,mysql->unix_socket,0))?true:false;
}

const char *mysql_character_set_name(MYSQL *mysql) {
	debugFunction();
	return "latin1";
}

int mysql_set_character_set(MYSQL *mysql, const char *csname) {
	debugFunction();
	// FIXME: implement this somehow
	return 0;
}



void mysql_debug(const char *debug) {
	// don't do anything...
}

int mysql_dump_debug_info(MYSQL *mysql) {
	debugFunction();
	return unknownError(mysql);
}



int mysql_create_db(MYSQL *mysql, const char *db) {
	debugFunction();
	return unknownError(mysql);
}

int mysql_select_db(MYSQL *mysql, const char *db) {
	debugFunction();
	return unknownError(mysql);
}

int mysql_drop_db(MYSQL *mysql, const char *db) {
	debugFunction();
	return unknownError(mysql);
}

MYSQL_RES *mysql_list_dbs(MYSQL *mysql, const char *wild) {
	debugFunction();
	return NULL;
}

MYSQL_RES *mysql_list_tables(MYSQL *mysql, const char *wild) {
	debugFunction();
	return NULL;
}



unsigned long mysql_escape_string(char *to, const char *from,
					unsigned long length) {
	debugFunction();
	return mysql_real_escape_string(NULL,to,from,length);
}

char *mysql_odbc_escape_string(MYSQL *mysql, char *to,
				unsigned long to_length,
				const char *from,
				unsigned long from_length,
				void *param,
				char *(*extend_buffer)
					(void *, char *to,
					unsigned long *length)) {
	debugFunction();
	// FIXME: implement this
	return NULL;
}

void myodbc_remove_escape(MYSQL *mysql, char *name) {
	debugFunction();
	// FIXME: implement this
}

unsigned long mysql_real_escape_string(MYSQL *mysql, char *to,
					const char *from,
					unsigned long length) {
	debugFunction();

	if (mysql && charstring::compare(mysql->sqlrcon->identify(),"mysql")) {
		rawbuffer::copy(to,from,length);
		to[length]=(char)NULL;
		return length;
	}
	
	unsigned long	fromindex=0;
	unsigned long	toindex=0;
	while (fromindex<length) {
		if (from[fromindex]=='\'') {
			to[toindex++]='\\';
			to[toindex++]='\'';
		} else if (from[fromindex]=='\"') {
			to[toindex++]='\\';
			to[toindex++]='"';
		} else if (from[fromindex]=='\n') {
			to[toindex++]='\\';
			to[toindex++]='n';
		} else if (from[fromindex]=='\r') {
			to[toindex++]='\\';
			to[toindex++]='r';
		} else if (from[fromindex]=='\\') {
			to[toindex++]='\\';
			to[toindex++]='\\';
		} else if (from[fromindex]==26) {
			to[toindex++]='\\';
			to[toindex++]='Z';
		} else {
			to[toindex++]=from[fromindex];
		}
		fromindex++;
	}
	to[toindex]=(char)NULL;
	return toindex;
}

int mysql_query(MYSQL *mysql, const char *query) {
	debugFunction();
	return mysql_real_query(mysql,query,charstring::length(query));
}

int mysql_send_query(MYSQL *mysql, const char *query, unsigned int length) {
	debugFunction();
	// FIXME: looks like this sends a query in the background then returns
	// so we can do something else in the foreground.
	return mysql_real_query(mysql,query,length);
}

int mysql_read_query_result(MYSQL *mysql) {
	debugFunction();
	// FIXME: looks like this checks to see if a query sent with
	// mysql_send_query has finished or not
	return 0;
}

int mysql_real_query(MYSQL *mysql, const char *query, unsigned long length) {
	debugFunction();
	mysql->currentstmt=mysql_prepare(mysql,query,length);
	return mysql_execute(mysql->currentstmt);
}

char *mysql_info(MYSQL *mysql) {
	debugFunction();
	return "";
}

my_ulonglong mysql_insert_id(MYSQL *mysql) {
	debugFunction();
	// FIXME: for mysql db's you can call "select last_insert_id()"
	// for other db's, ????
	return 0;
}


MYSQL_RES *mysql_store_result(MYSQL *mysql) {
	debugFunction();
	return mysql->currentstmt->result;
}

MYSQL_RES *mysql_use_result(MYSQL *mysql) {
	debugFunction();
	return mysql_store_result(mysql);
}

void mysql_free_result(MYSQL_RES *result) {
	debugFunction();
	if (result) {
		delete result->sqlrcur;
		if (result->fields) {
			delete[] result->fields;
			delete[] result->lengths;
		}
		delete result;
	}
}

my_bool mysql_more_results(MYSQL *mysql) {
	debugFunction();
	return false;
}

int mysql_next_result(MYSQL *mysql) {
	return -1;
}


MYSQL_RES *mysql_list_fields(MYSQL *mysql, const char *table,
						const char *wild) {
	debugFunction();
	// FIXME: implement this
	return NULL;
}

unsigned int mysql_num_fields(MYSQL_RES *result) {
	debugFunction();
	return result->sqlrcur->colCount();
}

MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result) {
	debugFunction();
	if (result->currentfield>=
		(MYSQL_FIELD_OFFSET)result->sqlrcur->colCount()) {
		return NULL;
	}
	return &result->fields[result->currentfield++];
}

MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *result) {
	debugFunction();
	result->currentfield=0;
	return result->fields;
}

MYSQL_FIELD *mysql_fetch_field_direct(MYSQL_RES *result, unsigned int fieldnr) {
	debugFunction();
	if (fieldnr>(unsigned int)result->sqlrcur->colCount()) {
		return NULL;
	}
	// FIXME: it's possible that we shouldn't increment the fieldnr here
	result->currentfield=fieldnr+1;
	return &result->fields[fieldnr];
}

unsigned long *mysql_fetch_lengths(MYSQL_RES *result) {
	debugFunction();
	uint32_t	*lengths=result->sqlrcur->
					getRowLengths(result->previousrow);
	for (uint32_t i=0; i<result->sqlrcur->colCount(); i++) {
		result->lengths[i]=(unsigned long)lengths[i];
	}
	return result->lengths;
}

unsigned int mysql_field_count(MYSQL *mysql) {
	debugFunction();
	return mysql_num_fields(mysql->currentstmt->result);
}

MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result,
					MYSQL_FIELD_OFFSET offset) {
	debugFunction();
	MYSQL_FIELD_OFFSET	oldoffset=result->currentfield;
	result->currentfield=offset;
	return oldoffset;
}

MYSQL_FIELD_OFFSET mysql_field_tell(MYSQL_RES *result) {
	debugFunction();
	return result->currentfield;
}


my_ulonglong mysql_num_rows(MYSQL_RES *result) {
	debugFunction();
	return result->sqlrcur->rowCount();
}

my_ulonglong mysql_affected_rows(MYSQL *mysql) {
	debugFunction();
	return mysql_stmt_affected_rows(mysql->currentstmt);
}


// FIXME: MYSQL_ROW_OFFSET is (or could be) smaller than my_ulonglong
MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET offset) {
	debugFunction();
	result->previousrow=result->currentrow;
	result->currentrow=(my_ulonglong)offset;
	return (MYSQL_ROW_OFFSET)result->previousrow;
}

// FIXME: MYSQL_ROW_OFFSET is (or could be) smaller than my_ulonglong
MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *result) {
	debugFunction();
	return (MYSQL_ROW_OFFSET)result->currentrow;
}

void mysql_data_seek(MYSQL_RES *result, my_ulonglong offset) {
	debugFunction();
	result->previousrow=result->currentrow;
	result->currentrow=offset;
}

MYSQL_ROW mysql_fetch_row(MYSQL_RES *result) {
	debugFunction();
	MYSQL_ROW	retval=
		(MYSQL_ROW)result->sqlrcur->getRow(result->currentrow);
	if (retval) {
		result->previousrow=result->currentrow;
		result->currentrow++;
	}
	return retval;
}

my_bool mysql_eof(MYSQL_RES *result) {
	debugFunction();
	my_ulonglong	rowcount=(my_ulonglong)result->sqlrcur->rowCount();
	return (!rowcount || result->currentrow>=rowcount);
}


unsigned int mysql_warning_count(MYSQL *mysql) {
	debugFunction();
	return 0;
}

unsigned int mysql_errno(MYSQL *mysql) {
	debugFunction();
	if (mysql && mysql->currentstmt && mysql->currentstmt->result) {
		return mysql->currentstmt->result->errorno;
	}
	return mysql->errorno;
}

const char *mysql_error(MYSQL *mysql) {
	debugFunction();
	const char	*err;
	if (mysql && mysql->currentstmt && mysql->currentstmt->result &&
					mysql->currentstmt->result->sqlrcur) {
		err=mysql->currentstmt->result->sqlrcur->errorMessage();
	} else {
		err=mysql->error;
	}
	return (err)?err:"";
}

const char *mysql_sqlstate(MYSQL *mysql) {
	debugFunction();
	return "";
}


my_bool mysql_commit(MYSQL *mysql) {
	debugFunction();
	return mysql->sqlrcon->commit();
}

my_bool mysql_rollback(MYSQL *mysql) {
	debugFunction();
	return mysql->sqlrcon->rollback();
}

my_bool mysql_autocommit(MYSQL *mysql, my_bool mode) {
	debugFunction();
	return (mode)?mysql->sqlrcon->autoCommitOn():
				mysql->sqlrcon->autoCommitOff();
}







MYSQL_STMT *mysql_prepare(MYSQL *mysql, const char *query,
					unsigned long length) {
	debugPrintf(query);
	debugPrintf("\n");
	MYSQL_STMT	*stmt=new MYSQL_STMT;
	stmt->result=new MYSQL_RES;
	stmt->result->sqlrcur=new sqlrcursor(mysql->sqlrcon);
	stmt->result->sqlrcur->copyReferences();
	stmt->result->errorno=0;
	stmt->result->fields=NULL;
	stmt->result->lengths=NULL;
	stmt->result->sqlrcur->prepareQuery(query,length);
	return stmt;
}

my_bool mysql_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind) {
	debugFunction();

	unsigned long	paramcount=mysql_param_count(stmt);
	for (unsigned long i=0; i<paramcount; i++) {

		char		*variable=charstring::parseNumber((uint32_t)i);
		sqlrcursor	*cursor=stmt->result->sqlrcur;
		switch (bind[i].buffer_type) {
			case MYSQL_TYPE_NULL: {
				cursor->inputBind(variable,(char *)NULL);
				break;
			}
			case MYSQL_TYPE_VAR_STRING:
			case MYSQL_TYPE_STRING:
			case MYSQL_TYPE_TIMESTAMP:
			case MYSQL_TYPE_DATE:
			case MYSQL_TYPE_TIME:
			case MYSQL_TYPE_DATETIME:
			case MYSQL_TYPE_NEWDATE: {
				char	*value=(char *)bind[i].buffer;
				cursor->inputBind(variable,value);
				break;
			}
			case MYSQL_TYPE_DECIMAL:
			case MYSQL_TYPE_FLOAT:
			case MYSQL_TYPE_DOUBLE: {
				double	value=*((double *)bind[i].buffer);
				// FIXME: precision/scale???
				cursor->inputBind(variable,value,0,0);
				break;
			}
			case MYSQL_TYPE_TINY:
			case MYSQL_TYPE_SHORT:
			case MYSQL_TYPE_LONG:
			case MYSQL_TYPE_YEAR: {
				long	value=*((long *)bind[i].buffer);
				cursor->inputBind(variable,value);
				break;
			}
			case MYSQL_TYPE_LONGLONG:
			case MYSQL_TYPE_INT24: {
				// FIXME: what should I do here?
				return false;
				break;
			}
			case MYSQL_TYPE_TINY_BLOB:
			case MYSQL_TYPE_MEDIUM_BLOB:
			case MYSQL_TYPE_LONG_BLOB:
			case MYSQL_TYPE_BLOB: {
				char		*value=(char *)bind[i].buffer;
				unsigned long	size=*(bind[i].length);
				cursor->inputBindBlob(variable,value,size);
				break;
			}
			case MYSQL_TYPE_ENUM:
			case MYSQL_TYPE_SET:
			case MYSQL_TYPE_GEOMETRY: {
				// FIXME: what should I do here?
				return false;
				break;
			}
			default: {
				// FIXME: what should I do here?
				return false;
			}
		}
	}
	return true;
}

my_bool mysql_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) {
	debugFunction();
	stmt->resultbinds=bind;
	return true;
}

static enum enum_field_types	mysqltypemap[]={
	// "UNKNOWN"
	MYSQL_TYPE_NULL,
	// addded by freetds
	// "CHAR"
	MYSQL_TYPE_STRING,
	// "INT"
	MYSQL_TYPE_LONG,
	// "SMALLINT"
	MYSQL_TYPE_SHORT,
	// "TINYINT"
	MYSQL_TYPE_TINY,
	// "MONEY"
	MYSQL_TYPE_DECIMAL,
	// "DATETIME"
	MYSQL_TYPE_DATETIME,
	// "NUMERIC"
	MYSQL_TYPE_DECIMAL,
	// "DECIMAL"
	MYSQL_TYPE_DECIMAL,
	// "SMALLDATETIME"
	MYSQL_TYPE_DATETIME,
	// "SMALLMONEY"
	MYSQL_TYPE_DECIMAL,
	// "IMAGE"
	MYSQL_TYPE_BLOB,
	// "BINARY"
	MYSQL_TYPE_BLOB,
	// "BIT"
	MYSQL_TYPE_TINY,
	// "REAL"
	MYSQL_TYPE_DECIMAL,
	// "FLOAT"
	MYSQL_TYPE_FLOAT,
	// "TEXT"
	MYSQL_TYPE_STRING,
	// "VARCHAR"
	MYSQL_TYPE_VAR_STRING,
	// "VARBINARY"
	MYSQL_TYPE_BLOB,
	// "LONGCHAR"
	MYSQL_TYPE_BLOB,
	// "LONGBINARY"
	MYSQL_TYPE_BLOB,
	// "LONG"
	MYSQL_TYPE_BLOB,
	// "ILLEGAL"
	MYSQL_TYPE_NULL,
	// "SENSITIVITY"
	MYSQL_TYPE_STRING,
	// "BOUNDARY"
	MYSQL_TYPE_STRING,
	// "VOID"
	MYSQL_TYPE_NULL,
	// "USHORT"
	MYSQL_TYPE_SHORT,
	// added by lago
	// "UNDEFINED"
	MYSQL_TYPE_NULL,
	// "DOUBLE"
	MYSQL_TYPE_DOUBLE,
	// "DATE"
	MYSQL_TYPE_DATETIME,
	// "TIME"
	MYSQL_TYPE_DATETIME,
	// "TIMESTAMP"
	MYSQL_TYPE_TIMESTAMP,
	// added by msql
	// "UINT"
	MYSQL_TYPE_LONG,
	// "LASTREAL"
	MYSQL_TYPE_DECIMAL,
	// added by mysql
	// "STRING"
	MYSQL_TYPE_STRING,
	// "VARSTRING"
	MYSQL_TYPE_VAR_STRING,
	// "LONGLONG"
	MYSQL_TYPE_LONGLONG,
	// "MEDIUMINT"
	MYSQL_TYPE_INT24,
	// "YEAR"
	MYSQL_TYPE_YEAR,
	// "NEWDATE"
	MYSQL_TYPE_NEWDATE,
	// "NULL"
	MYSQL_TYPE_NULL,
	// "ENUM"
	MYSQL_TYPE_ENUM,
	// "SET"
	MYSQL_TYPE_SET,
	// "TINYBLOB"
	MYSQL_TYPE_TINY_BLOB,
	// "MEDIUMBLOB"
	MYSQL_TYPE_MEDIUM_BLOB,
	// "LONGBLOB"
	MYSQL_TYPE_LONG_BLOB,
	// "BLOB"
	MYSQL_TYPE_BLOB,
	// added by oracle
	// "VARCHAR2"
	MYSQL_TYPE_VAR_STRING,
	// "NUMBER"
	MYSQL_TYPE_DECIMAL,
	// "ROWID"
	MYSQL_TYPE_LONGLONG,
	// "RAW"
	MYSQL_TYPE_BLOB,
	// "LONG_RAW"
	MYSQL_TYPE_BLOB,
	// "MLSLABEL"
	MYSQL_TYPE_BLOB,
	// "CLOB"
	MYSQL_TYPE_BLOB,
	// "BFILE"
	MYSQL_TYPE_BLOB,
	// added by odbc
	// "BIGINT"
	MYSQL_TYPE_LONGLONG,
	// "INTEGER"
	MYSQL_TYPE_LONG,
	// "LONGVARBINARY"
	MYSQL_TYPE_BLOB,
	// "LONGVARCHAR"
	MYSQL_TYPE_BLOB,
	// added by db2
	// "GRAPHIC"
	MYSQL_TYPE_BLOB,
	// "VARGRAPHIC"
	MYSQL_TYPE_BLOB,
	// "LONGVARGRAPHIC"
	MYSQL_TYPE_BLOB,
	// "DBCLOB"
	MYSQL_TYPE_BLOB,
	// "DATALINK"
	MYSQL_TYPE_STRING,
	// "USER_DEFINED_TYPE"
	MYSQL_TYPE_STRING,
	// "SHORT_DATATYPE"
	MYSQL_TYPE_SHORT,
	// "TINY_DATATYPE"
	MYSQL_TYPE_TINY,
	// added by firebird
	// "D_FLOAT"
	MYSQL_TYPE_DOUBLE,
	// "ARRAY"
	MYSQL_TYPE_SET,
	// "QUAD"
	MYSQL_TYPE_SET,
	// "INT64"
	MYSQL_TYPE_LONGLONG,
	// "DOUBLE PRECISION"
	MYSQL_TYPE_DOUBLE,
	// added by postgresql
	// "NAME"
	MYSQL_TYPE_STRING,
	// "INT8"
	MYSQL_TYPE_LONG,
	// "INT2"
	MYSQL_TYPE_SHORT,
	// "INT2VECTOR"
	MYSQL_TYPE_SET,
	// "INT4"
	MYSQL_TYPE_LONG,
	// "REGPROC"
	MYSQL_TYPE_LONG,
	// "OID"
	MYSQL_TYPE_LONG,
	// "TID"
	MYSQL_TYPE_LONG,
	// "XID"
	MYSQL_TYPE_LONG,
	// "CID"
	MYSQL_TYPE_LONG,
	// "OIDVECTOR"
	MYSQL_TYPE_SET,
	// "SMGR"
	MYSQL_TYPE_STRING,
	// "POINT"
	MYSQL_TYPE_STRING,
	// "LSEG"
	MYSQL_TYPE_STRING,
	// "PATH"
	MYSQL_TYPE_STRING,
	// "BOX"
	MYSQL_TYPE_STRING,
	// "POLYGON"
	MYSQL_TYPE_STRING,
	// "LINE"
	MYSQL_TYPE_STRING,
	// "LINE_ARRAY"
	MYSQL_TYPE_SET,
	// "FLOAT4"
	MYSQL_TYPE_FLOAT,
	// "FLOAT8"
	MYSQL_TYPE_DOUBLE,
	// "ABSTIME"
	MYSQL_TYPE_LONG,
	// "RELTIME"
	MYSQL_TYPE_LONG,
	// "TINTERVAL"
	MYSQL_TYPE_LONG,
	// "CIRCLE"
	MYSQL_TYPE_STRING,
	// "CIRCLE_ARRAY"
	MYSQL_TYPE_SET,
	// "MONEY_ARRAY"
	MYSQL_TYPE_SET,
	// "MACADDR"
	MYSQL_TYPE_STRING,
	// "INET"
	MYSQL_TYPE_STRING,
	// "CIDR"
	MYSQL_TYPE_STRING,
	// "BOOL_ARRAY"
	MYSQL_TYPE_SET,
	// "BYTEA_ARRAY"
	MYSQL_TYPE_SET,
	// "CHAR_ARRAY"
	MYSQL_TYPE_SET,
	// "NAME_ARRAY"
	MYSQL_TYPE_SET,
	// "INT2_ARRAY"
	MYSQL_TYPE_SET,
	// "INT2VECTOR_ARRAY"
	MYSQL_TYPE_SET,
	// "INT4_ARRAY"
	MYSQL_TYPE_SET,
	// "REGPROC_ARRAY"
	MYSQL_TYPE_SET,
	// "TEXT_ARRAY"
	MYSQL_TYPE_SET,
	// "OID_ARRAY"
	MYSQL_TYPE_SET,
	// "TID_ARRAY"
	MYSQL_TYPE_SET,
	// "XID_ARRAY"
	MYSQL_TYPE_SET,
	// "CID_ARRAY"
	MYSQL_TYPE_SET,
	// "OIDVECTOR_ARRAY"
	MYSQL_TYPE_SET,
	// "BPCHAR_ARRAY"
	MYSQL_TYPE_SET,
	// "VARCHAR_ARRAY"
	MYSQL_TYPE_SET,
	// "INT8_ARRAY"
	MYSQL_TYPE_SET,
	// "POINT_ARRAY"
	MYSQL_TYPE_SET,
	// "LSEG_ARRAY"
	MYSQL_TYPE_SET,
	// "PATH_ARRAY"
	MYSQL_TYPE_SET,
	// "BOX_ARRAY"
	MYSQL_TYPE_SET,
	// "FLOAT4_ARRAY"
	MYSQL_TYPE_SET,
	// "FLOAT8_ARRAY"
	MYSQL_TYPE_SET,
	// "ABSTIME_ARRAY"
	MYSQL_TYPE_SET,
	// "RELTIME_ARRAY"
	MYSQL_TYPE_SET,
	// "TINTERVAL_ARRAY"
	MYSQL_TYPE_SET,
	// "POLYGON_ARRAY"
	MYSQL_TYPE_SET,
	// "ACLITEM"
	MYSQL_TYPE_SET,
	// "ACLITEM_ARRAY"
	MYSQL_TYPE_SET,
	// "MACADDR_ARRAY"
	MYSQL_TYPE_SET,
	// "INET_ARRAY"
	MYSQL_TYPE_SET,
	// "CIDR_ARRAY"
	MYSQL_TYPE_SET,
	// "BPCHAR"
	MYSQL_TYPE_STRING,
	// "TIMESTAMP_ARRAY"
	MYSQL_TYPE_SET,
	// "DATE_ARRAY"
	MYSQL_TYPE_SET,
	// "TIME_ARRAY"
	MYSQL_TYPE_SET,
	// "TIMESTAMPTZ"
	MYSQL_TYPE_STRING,
	// "TIMESTAMPTZ_ARRAY"
	MYSQL_TYPE_SET,
	// "INTERVAL"
	MYSQL_TYPE_LONG,
	// "INTERVAL_ARRAY"
	MYSQL_TYPE_SET,
	// "NUMERIC_ARRAY"
	MYSQL_TYPE_SET,
	// "TIMETZ"
	MYSQL_TYPE_STRING,
	// "TIMETZ_ARRAY"
	MYSQL_TYPE_SET,
	// "BIT_ARRAY"
	MYSQL_TYPE_SET,
	// "VARBIT"
	MYSQL_TYPE_STRING,
	// "VARBIT_ARRAY"
	MYSQL_TYPE_SET,
	// "REFCURSOR"
	MYSQL_TYPE_LONG,
	// "REFCURSOR_ARRAY"
	MYSQL_TYPE_SET,
	// "REGPROCEDURE"
	MYSQL_TYPE_LONG,
	// "REGOPER"
	MYSQL_TYPE_LONG,
	// "REGOPERATOR"
	MYSQL_TYPE_LONG,
	// "REGCLASS"
	MYSQL_TYPE_LONG,
	// "REGTYPE"
	MYSQL_TYPE_LONG,
	// "REGPROCEDURE_ARRAY"
	MYSQL_TYPE_SET,
	// "REGOPER_ARRAY"
	MYSQL_TYPE_SET,
	// "REGOPERATOR_ARRAY"
	MYSQL_TYPE_SET,
	// "REGCLASS_ARRAY"
	MYSQL_TYPE_SET,
	// "REGTYPE_ARRAY"
	MYSQL_TYPE_SET,
	// "RECORD"
	MYSQL_TYPE_SET,
	// "CSTRING"
	MYSQL_TYPE_STRING,
	// "ANY"
	MYSQL_TYPE_STRING,
	// "ANYARRAY"
	MYSQL_TYPE_SET,
	// "TRIGGER"
	MYSQL_TYPE_STRING,
	// "LANGUAGE_HANDLER"
	MYSQL_TYPE_STRING,
	// "INTERNAL"
	MYSQL_TYPE_STRING,
	// "OPAQUE"
	MYSQL_TYPE_STRING,
	// "ANYELEMENT"
	MYSQL_TYPE_STRING,
	// "PG_TYPE"
	MYSQL_TYPE_STRING,
	// "PG_ATTRIBUTE"
	MYSQL_TYPE_STRING,
	// "PG_PROC"
	MYSQL_TYPE_STRING,
	// "PG_CLASS"
	MYSQL_TYPE_STRING,
};

enum enum_field_types map_col_type(const char *columntype) {
	debugFunction();
	for (int index=0; datatypestring[index]; index++) {
		if (!charstring::compare(datatypestring[index],columntype)) {
			return mysqltypemap[index];
		}
	}
	return MYSQL_TYPE_NULL;
}

int mysql_execute(MYSQL_STMT *stmt) {
	debugFunction();

	stmt->result->previousrow=0;
	stmt->result->currentrow=0;
	stmt->result->currentfield=0;
	sqlrcursor	*sqlrcur=stmt->result->sqlrcur;

	int	retval=!sqlrcur->executeQuery();

	delete[] stmt->result->fields;
	delete[] stmt->result->lengths;

	uint32_t	colcount=sqlrcur->colCount();
	if (colcount) {

		MYSQL_FIELD	*fields=new MYSQL_FIELD[colcount];
		stmt->result->fields=fields;

		stmt->result->lengths=new unsigned long[colcount];

		for (uint32_t i=0; i<colcount; i++) {

			fields[i].name=const_cast<char *>(
					sqlrcur->getColumnName(i));
			fields[i].table="";
			fields[i].def="";

			#if defined(COMPAT_MYSQL_4_0) || \
				defined(COMPAT_MYSQL_4_1) || \
				defined(COMPAT_MYSQL_5_0) || \
				defined(COMPAT_MYSQL_5_1)
  			fields[i].org_table="";
  			fields[i].db="";
			#if defined(COMPAT_MYSQL_4_1) || \
				defined(COMPAT_MYSQL_5_0) || \
				defined(COMPAT_MYSQL_5_1)
  			fields[i].catalog="";
  			fields[i].org_name=const_cast<char *>(
						sqlrcur->getColumnName(i));
			fields[i].name_length=
				charstring::length(fields[i].name);
			fields[i].org_name_length=
				charstring::length(fields[i].org_name);
			fields[i].table_length=
				charstring::length(fields[i].table);
			fields[i].org_table_length=
				charstring::length(fields[i].org_table);
			fields[i].db_length=
				charstring::length(fields[i].db);
			fields[i].catalog_length=
				charstring::length(fields[i].catalog);
			fields[i].def_length=
				charstring::length(fields[i].def);
			// FIXME: need a character set number here
			fields[i].charsetnr=0;
			#endif
			#endif

			// figure out the column type
			const char	*columntypestring=
						sqlrcur->getColumnType(i);
			enum enum_field_types	columntype=
					map_col_type(columntypestring);
			fields[i].type=columntype;

			fields[i].length=sqlrcur->getColumnLength(i);
			fields[i].max_length=sqlrcur->getLongest(i);

			// figure out the flags
			unsigned int	flags=0;
			if (sqlrcur->getColumnIsNullable(i)) {
				#define NOT_NULL_FLAG	1
				flags|=NOT_NULL_FLAG;
			}
			if (sqlrcur->getColumnIsPrimaryKey(i)) {
				#define PRI_KEY_FLAG	2
				flags|=PRI_KEY_FLAG;
			}
			if (sqlrcur->getColumnIsUnique(i)) {
				#define UNIQUE_KEY_FLAG 4
				flags|=UNIQUE_KEY_FLAG;
			}
			if (sqlrcur->getColumnIsPartOfKey(i)) {
				#define MULTIPLE_KEY_FLAG 8
				flags|=MULTIPLE_KEY_FLAG;
			}
			if (columntype==MYSQL_TYPE_TINY_BLOB ||
				columntype==MYSQL_TYPE_MEDIUM_BLOB ||
				columntype==MYSQL_TYPE_LONG_BLOB ||
				columntype==MYSQL_TYPE_BLOB) {
				#define BLOB_FLAG	16
				flags|=BLOB_FLAG;
			}
			if (sqlrcur->getColumnIsUnsigned(i) ||
				isUnsignedTypeChar(columntypestring)) {
				#define UNSIGNED_FLAG	32
				flags|=UNSIGNED_FLAG;
			}
			if (sqlrcur->getColumnIsZeroFilled(i)) {
				#define ZEROFILL_FLAG	64
				flags|=ZEROFILL_FLAG;
			}
			if (sqlrcur->getColumnIsBinary(i) ||
				isBinaryTypeChar(columntypestring)) {
				#define BINARY_FLAG	128
				flags|=BINARY_FLAG;
			}
			if (columntype==MYSQL_TYPE_ENUM) {
				#define ENUM_FLAG	256
				flags|=ENUM_FLAG;
			}
			if (sqlrcur->getColumnIsAutoIncrement(i)) {
				#define AUTO_INCREMENT_FLAG 512
				flags|=AUTO_INCREMENT_FLAG;
			}
			if (columntype==MYSQL_TYPE_TIMESTAMP) {
				#define TIMESTAMP_FLAG	1024
				flags|=TIMESTAMP_FLAG;
			}
			if (columntype==MYSQL_TYPE_SET) {
				#define SET_FLAG	2048
				flags|=SET_FLAG;
			}
			if (isNumberTypeChar(columntypestring)) {
				#define NUM_FLAG	32768
				flags|=NUM_FLAG;
			}
			// Presumably these don't matter...
			// Intern; Part of some key
			//#define PART_KEY_FLAG	16384
			// Intern: Group field
			//#define GROUP_FLAG	32768
			// Intern: Used by sql_yacc
			//#define UNIQUE_FLAG	65536
			fields[i].flags=flags;

			fields[i].decimals=sqlrcur->getColumnPrecision(i);
		}
	} else {
		stmt->result->fields=NULL;
		stmt->result->lengths=NULL;
	}

	return retval;
}

unsigned long mysql_param_count(MYSQL_STMT *stmt) {
	debugFunction();
	return stmt->result->sqlrcur->countBindVariables();
}

MYSQL_RES *mysql_param_result(MYSQL_STMT *stmt) {
	debugFunction();
	// FIXME: The MySQL docs don't even explain this one
	return NULL;
}



int mysql_fetch(MYSQL_STMT *stmt) {
	debugFunction();

	MYSQL_ROW	row=mysql_fetch_row(stmt->result);
	if (!row) {
		return MYSQL_NO_DATA;
	}

	uint32_t	*lengths=stmt->result->sqlrcur->
				getRowLengths(stmt->result->previousrow);

	for (uint32_t i=0; i<stmt->result->sqlrcur->colCount(); i++) {
		*(stmt->resultbinds[i].length)=lengths[i];
		if (!row[i]) {
			*(stmt->resultbinds[i].is_null)=true;
		} else {
			*(stmt->resultbinds[i].is_null)=false;
			rawbuffer::copy(stmt->resultbinds[i].buffer,
							row[i],lengths[i]);
		}
		stmt->resultbinds[i].buffer[lengths[i]]=(char)NULL;

		// FIXME: I think I'm supposed to convert to
		// some other type based on the column type...
		stmt->resultbinds[i].buffer_type=MYSQL_TYPE_STRING,
		stmt->resultbinds[i].buffer_length=lengths[i];
	}
	return 0;
}

int mysql_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind,
			unsigned int column, unsigned long offset) {
	debugFunction();
	// FIXME: The MySQL docs don't even explain this one.
	return 0;
}



MYSQL_RES *mysql_get_metadata(MYSQL_STMT *stmt) {
	debugFunction();
	return stmt->result;
}



my_bool mysql_send_long_data(MYSQL_STMT *stmt,
				unsigned int parameter_number,
				const char *data, unsigned long length) {
	debugFunction();
	return false;
}



my_ulonglong mysql_stmt_num_rows(MYSQL_STMT *stmt) {
	debugFunction();
	return mysql_num_rows(stmt->result);
}

my_ulonglong mysql_stmt_affected_rows(MYSQL_STMT *stmt) {
	debugFunction();
	return stmt->result->sqlrcur->affectedRows();
}

MYSQL_ROW_OFFSET mysql_stmt_row_seek(MYSQL_STMT *stmt,
					MYSQL_ROW_OFFSET offset) {
	debugFunction();
	return mysql_row_seek(stmt->result,offset);
}

MYSQL_ROW_OFFSET mysql_stmt_row_tell(MYSQL_STMT *stmt) {
	debugFunction();
	return mysql_row_tell(stmt->result);
}

void mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong offset) {
	debugFunction();
	mysql_data_seek(stmt->result,offset);
}



my_bool mysql_stmt_close(MYSQL_STMT *stmt) {
	debugFunction();
	if (stmt) {
		// It would seem like we'd want to call mysql_free_result here,
		// but the mysql client calls it manually before calling
		// mysql_close(), so presumably that's how apps should act.
		//mysql_free_result(stmt->result);
		delete stmt;
		stmt=NULL;
	}
	return true;
}



unsigned int mysql_stmt_errno(MYSQL_STMT *stmt) {
	debugFunction();
	return stmt->result->errorno;
}

const char *mysql_stmt_error(MYSQL_STMT *stmt) {
	debugFunction();
	return stmt->result->sqlrcur->errorMessage();
}

const char *mysql_stmt_sqlstate(MYSQL_STMT *stmt) {
	debugFunction();
	return "";
}



int mysql_stmt_store_result(MYSQL_STMT *stmt) {
	debugFunction();
	return 0;
}

my_bool mysql_stmt_free_result(MYSQL_STMT *stmt) {
	debugFunction();
	mysql_free_result(stmt->result);
	return true;
}



my_bool mysql_stmt_reset(MYSQL_STMT *stmt) {
	debugFunction();
	stmt->result->sqlrcur->clearBinds();
	return true;
}

void mysql_server_init(int argc, char **argv, char **groups) {
	debugFunction();
	return mysql_library_init(argc,argv,groups);
}

void mysql_library_init(int argc, char **argv, char **groups) {
	debugFunction();
	// FIXME: do something?
}

void mysql_server_end() {
	debugFunction();
	return mysql_library_end();
}

void mysql_library_end() {
	debugFunction();
	// FIXME: do something?
}

int unknownError(MYSQL *mysql) {
	mysql->errorno=CR_UNKNOWN_ERROR;
	mysql->error="Unknown MySQL error";
	return CR_UNKNOWN_ERROR;
}

}
